package xyz.zq.sf.gateway.filter;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author hzq
 * @date 2021-04-27 10:36
 * @see org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory
 */
@Component
@RequiredArgsConstructor
public class VerificationCodeGatewayFilter extends AbstractGatewayFilterFactory {

    private final ServerCodecConfigurer codecConfigurer;

    @Override
    public GatewayFilter apply(Object config) {
        return (ServerWebExchange exchange, GatewayFilterChain chain) -> {
            // 判断请求地址
            AtomicReference<String> phone = new AtomicReference<>();
            AtomicReference<String> code = new AtomicReference<>();
            if (Objects.equals(exchange.getRequest().getMethod(), HttpMethod.POST)) {
                ServerRequest serverRequest = ServerRequest.create(exchange, codecConfigurer.getReaders());
                MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
                Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
                        .flatMap(body -> {
                            Map<String, ?> map = new HashMap();
                            if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType)) {
                                map = HttpUtil.decodeParamMap(body, CharsetUtil.CHARSET_UTF_8);
                            }
                            if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) {
                                map = JSONObject.parseObject(body).getInnerMap();
                            }
                            phone.set(map.get("phone").toString());
                            code.set(map.get("code").toString());

                            // todo 验证验证码

                            // 电话号码验证码
                            if (StrUtil.isBlank(phone.get()) || StrUtil.isBlank(code.get())) {
//                                return Mono.error(new IllegalArgumentException("手机号码或验证码不能为空"));
                            }
                            return Mono.just(body);
                        });
                BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
                HttpHeaders headers = new HttpHeaders();
                headers.putAll(exchange.getRequest().getHeaders());
                headers.remove(HttpHeaders.CONTENT_LENGTH);
                CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
                return bodyInserter.insert(outputMessage, new BodyInserterContext())
                        .then(Mono.defer(() -> chain.filter(exchange.mutate().request(decorate(exchange, headers, outputMessage)).build())));
            }
            return chain.filter(exchange);
        };
    }

    ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage) {
        return new ServerHttpRequestDecorator(exchange.getRequest()) {
            @Override
            public HttpHeaders getHeaders() {
                long contentLength = headers.getContentLength();
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.putAll(headers);
                if (contentLength > 0) {
                    httpHeaders.setContentLength(contentLength);
                } else {
                    httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                }
                return httpHeaders;
            }

            @Override
            public Flux<DataBuffer> getBody() {
                return outputMessage.getBody();
            }
        };
    }
}
